package org.spp.utilities;

import org.spp.voronoi.Centroid;

import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.FileWriter;
import java.io.File;

/**
 * QVoronoi.java
 * @author Ilio Catallo, Eleonora Ciceri
 * @version 1.0
 * @see VoronoiDiagram
 * 
 * Description: the class is on charge of calling the external program qvoronoi to obtain
 *              the facets adjacency for voronoi diagram with more than three centroids
 * 
 * Date: Jul 20, 2011
 */
public class QVoronoi {
    
    //Attributes
    private String execPath;
    private List<Centroid> centroids;
    
    /**
     * Constructor for the class QVoronoi
     * @param execPath 
     */
    public QVoronoi(String execPath){
        
        this.execPath = execPath;
    }
    
    /**
     * Method that execute the external call to qVoronoi and return the 
     * adjacency between centroids
     * @param centroids
     * @return facetsTpVertexes
     */
    public HashMap run(List<Centroid> centroids){
        
        this.centroids = centroids;

        // External call to qvoronoi 
        String qVoronoiResult = callQVoronoi();
        HashMap facetsToVertexes = parseQVoronoiResult(qVoronoiResult);

        return facetsToVertexes;
    }
    
    /**
     * Call the qvoronoi program and return its output as a String
     * @return voronoiOutput
     */
    private String callQVoronoi(){
        // Variables
        String fileName = "points";

        // Save to disk the coordinates of the centroids
        preparePointsFile(fileName);
        
        try{

            // Call qvoronoi
            String[] cmds = new String[]{execPath, "Qz","o", "TI", fileName};
            Process qvoronoi = Runtime.getRuntime().exec(cmds);

            // Read the STDOUT and save it in a String
            BufferedReader br = new BufferedReader(new InputStreamReader(qvoronoi.getInputStream()));
            StringBuffer sb = new StringBuffer();
            String line;

            // Convert the InputStream in a String
            while ((line = br.readLine()) != null)
                sb.append(line).append("\r");

            // Delete the points file
            File points = new File(fileName);
            boolean success = points.delete();
            if (!success)
                System.err.println("Errore nella cancellazione di points");
            
            return sb.toString();
            
        } catch (IOException e){}
        
        return null;
        
    }
    
    /**
     * Save to disk the input file for qvoronoi
     * @param fileName 
     */
    private void preparePointsFile(String fileName){
        //Variables
        int numDimensions = centroids.get(0).getCoordinates().length;
        
        try{
            
            //Open the file on disk
            PrintWriter out = new PrintWriter(new FileWriter(fileName));
            
            //Write the file on disk
            out.println(Integer.toString(numDimensions));
            out.println(Integer.toString(centroids.size()));
            
            for (int i = 0; i < centroids.size(); i++){

                double[] coordinates = centroids.get(i).getCoordinates();

                out.println(Double.toString(coordinates[0]));
                out.println(Double.toString(coordinates[1]));
                if (coordinates.length == 3)
                    out.println(Double.toString(coordinates[2]));

            }
            
            out.close();
        
        } catch (IOException e) {e.printStackTrace();}
                   
    }
    
    /**
     * Parse the output of qvoronoi and create the facetsToVertexes matrix
     * @param qVoronoiResult
     * @return facetsToVertexes
     */
    private HashMap parseQVoronoiResult(String qVoronoiResult){
        // Variables
        HashMap facetsToVertexes = new HashMap();
        
        String[] results = qVoronoiResult.split("\r");
        Integer numPoints = Integer.parseInt(results[1].split("\\s")[0]);
        
        // Build the facetsToVertexes matrix
        for (int i = 0; i < centroids.size(); i++){
            
            String[] facetsStr = results[2+numPoints+i].split("\\s");
            List<Integer> facets = new ArrayList<Integer>();
            
            for (int j = 1; j < facetsStr.length; j++){
                Integer vertex = Integer.parseInt(facetsStr[j]);
                if (vertex != 0)
                    facets.add(vertex);
            }
            
            facetsToVertexes.put(centroids.get(i), facets);
            
        }
        
        return facetsToVertexes;
        
    }
    
}
